/* ========================================================================= */
/* ------------------------------------------------------------------------- */
/*!
  \file			plginman.cc
  \date			May 2012
  \author		TNick

  \brief		Contains the implementation for PlgInMan class


*//*

 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Please read COPYING and README files in root folder
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

*/
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
//
//
//
//
/*  INCLUDES    ------------------------------------------------------------ */

#include	<QLibrary>
#include	<QSettings>
#include	<QDir>
#include	<QLibrary>

#include	<usermsg.h>

#include	<cpg/cpg.h>
#include	<cpg/cpglibrary.h>

#include	<cpg/plug-ins/plugin.h>

#include	"plginman.h"

/*  INCLUDES    ============================================================ */
//
//
//
//
/*  DEFINITIONS    --------------------------------------------------------- */

using namespace cpg;
using namespace cpg::PlugIns;

/*  DEFINITIONS    ========================================================= */
//
//
//
//
/*  DATA    ---------------------------------------------------------------- */

PlgInMan *		PlgInMan::uniq_ = NULL;

/*  DATA    ================================================================ */
//
//
//
//
/*  CLASS    --------------------------------------------------------------- */

/* ------------------------------------------------------------------------- */
QStringList		PlgInMan::startUpSuite		( void )
{
	QSettings	stg;
	QStringList sl = stg.value( "stg/startUpSuite" ).toStringList();

	return sl;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			PlgInMan::setStartUpSuite		( const QStringList & new_l )
{
	QSettings	stg;
	stg.setValue( "stg/startUpSuite", new_l );
	emit uniq_->startUpSuiteChanged( new_l );
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void			PlgInMan::end		( void )
{
	BBlocks::UserMsg		um;

	if ( uniq_ == NULL )
		return;


	/* loop in all plug-ins */
	int i = uniq_->plgin_list_.count() - 1;
	while ( i >= 0)
	{
		uniq_->plgin_list_.at( i )->systemClosing( um );
		unload( uniq_->plgin_list_.at( i ) );

		i--;
	}

	uniq_->plgin_list_.clear();
	uniq_->lib_list_.clear();

	if ( um.count() > 0 )
		um.show();

	delete uniq_;
	uniq_ = NULL;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void		PlgInMan::init		( void )
{
	if ( uniq_ != NULL )
		return;


	BBlocks::UserMsg			um;
	QStringList		sl = startUpSuite();
	bool			b_ok = true;
	PlugIn *		out_p;
	QString			s_item;

	/* creae the manager */
	uniq_ = new PlgInMan();

	/* load start-up suite */
	foreach( s_item, sl )
	{
		b_ok &= ( OUT_SUCCESS( fromFile( s_item, &out_p, um) ) );
	}

	/* show messages from plug-ins */
	um.show();

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
PlugIn*			PlgInMan::plugin			( int i )
{
	if ( i < uniq_->plgin_list_.count( ) )
	{
		return uniq_->plgin_list_.at( i );
	}
	return NULL;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
OUTCOME				PlgInMan::fromFile (
		const QString & s_file,
		PlugIn **out_p, BBlocks::UserMsg & um )
{
	QLibrary * 		plg_lib = new QLibrary();
	GetPlyGrInterf	the_func;

	*out_p = NULL;

	/* attempt to load the library */
	plg_lib->setFileName( s_file );
	if ( ! plg_lib->isLoaded() )
	{
		if ( ! plg_lib->load() )
		{
			um.add( BBlocks::UserMsgMgr::MSG_ERROR,	QObject::tr(
						"Could not load plugin %1.\n%2" )
					.arg( s_file )
					.arg( plg_lib->errorString() )
					);
			return OUTCOME_ERROR;
		}
	}

	the_func = (GetPlyGrInterf) plg_lib->resolve( GET_INTERF_FUN_NAME );
	if ( the_func )
	{
		static char ver[] = { CPG_VER_STR };
		PlugIn * ptr_plg = the_func( &ver[ 0 ], um );
		if ( ptr_plg == NULL )
		{
			um.add( BBlocks::UserMsgMgr::MSG_ERROR,	QObject::tr(
						"The plug-in %1 did not provide an "
						"interface uppon request." )
					.arg( plg_lib->fileName() )
					);
			plg_lib->unload();
			delete plg_lib;
			return OUTCOME_ERROR;
		}
		else
		{
			ptr_plg->setFile( plg_lib->fileName() );
			if ( ptr_plg->initComplete( um ) == false )
			{
				delete ptr_plg;
				plg_lib->unload();
				delete plg_lib;
				return OUTCOME_ERROR;
			}
			else
			{
				*out_p = ptr_plg;
				uniq_->lib_list_.append( plg_lib );
			}
		}
	}
	else
	{
		um.add( BBlocks::UserMsgMgr::MSG_ERROR,	QObject::tr(
					"The library %1 is not a valid plug-in.\n%2" )
				.arg( plg_lib->fileName() )
				.arg( plg_lib->errorString() )
				);
		plg_lib->unload();
		delete plg_lib;
		return OUTCOME_ERROR;
	}

	emit uniq_->plugInLoaded( *out_p );

	return OUTCOME_OK;

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
bool				PlgInMan::unload	( PlugIn * the_p )
{
	if ( the_p == NULL )
		return false;
	QLibrary * lib;

	/* find the plugin; reject the call if not found */
	int i = uniq_->plgin_list_.indexOf( the_p );
	if ( i != -1 )
	{
		Q_ASSERT( i < uniq_->lib_list_.count() );

		/* inform everybody */
		emit uniq_->plugInUnloaded( the_p );

		/* clean-up tasks */
		lib = uniq_->lib_list_.at( i );
		uniq_->plgin_list_.removeAt( i );
		uniq_->lib_list_.removeAt( i );
		delete the_p;
		delete lib;

		return true;
	}

	return false;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
OUTCOME				PlgInMan::makeStartUp	(
		PlugIn * the_p, BBlocks::UserMsg & um  )
{
	if ( uniq_->plgin_list_.indexOf( the_p ) == -1 )
	{
		uniq_->plgin_list_.append( the_p );
	}
	QString sf = the_p->file();
	return makeStartUp( sf, um );
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
OUTCOME				PlgInMan::remFromStartUp(
		const QString & the_p, BBlocks::UserMsg & um )
{

	if ( the_p.isEmpty() )
	{
		um.add( BBlocks::UserMsgMgr::MSG_ERROR, QObject::tr(
					"Attempt to remove start-up plug-in but"
					"no plug-in file provided.")
				);
		return OUTCOME_ERROR;
	}

	QStringList sl = startUpSuite();
	for ( int i = 0; i < sl.count(); i++ )
	{
		if ( 0 == sl.at( i ).compare( the_p, Qt::CaseInsensitive ) )
		{
			sl.removeAt( i );
			setStartUpSuite( sl );
			return OUTCOME_OK;
		}
	}
	um.add( BBlocks::UserMsgMgr::MSG_WARNING, QObject::tr(
				"Attempt to remove start-up plug-in - "
				"a file (%1) that is not part of start-up suite")
			.arg( the_p )
			);
	return OUTCOME_ERROR;

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
QList<PlgInMan::PlgInInfo> PlgInMan::listPlugIns		(
		QString & s_plg_path )
{
	QFileInfo			fi;
	PlugIn*				plgin;
	PlgInInfo			crt_info;
	QSettings			stg;
	bool				b_found;
	int					i_max;
	int					i;
	QString				s_tmp;
	QList<PlgInInfo>	plist;
	QStringList			st_s = startUpSuite();
	QString s_def = QCoreApplication::applicationDirPath() + "/plug-ins";
	QDir	d( stg.value( "plgin/dir", s_def ).toString() );
	if ( d.exists() == false )
	{
		d.setPath( s_def );
		if ( d.exists() == false )
		{
			s_plg_path = s_def;
			return plist;
		}
	}

	s_plg_path = d.absolutePath();
	QFileInfoList		fi_list = d.entryInfoList();

	foreach( fi, fi_list )		{
		if ( fi.isSymLink() )
			continue;
		crt_info.s_full_path = fi.absoluteFilePath();
		if ( QLibrary::isLibrary( crt_info.s_full_path ) )
		{
			crt_info.b_loaded = false;
			crt_info.p_interf = NULL;
			foreach( plgin, uniq_->plgin_list_ )		{
				if ( plgin->file_ == crt_info.s_full_path )
				{ /* it is loaded */
					crt_info.b_loaded = true;
					crt_info.s_name = plgin->name();
					crt_info.p_interf = plgin;
					break;
				}
			}
			if ( crt_info.b_loaded == false )
			{
				crt_info.s_name = fi.baseName();
			}
			crt_info.b_start_up = false;
			plist.append( crt_info );
		}
	}

	/* add plug-ins that are loaded frm outside of standard directory */
	crt_info.b_start_up = false;
	crt_info.b_loaded = true;
	foreach( plgin, uniq_->plgin_list_ )		{
		b_found = false;
		foreach( PlgInInfo itr,	plist )
		{
			if  ( QString::compare( itr.s_full_path, plgin->file(), Qt::CaseInsensitive ) == 0 )
			{
				b_found = true;
				break;
			}
		}
		if ( b_found == false )
		{
			crt_info.s_full_path = plgin->file();
			crt_info.s_name = plgin->name();
			crt_info.p_interf = plgin;
			plist.append( crt_info );
		}
	}

	/* add plug-ins from start-up suite that are not loaded; mark as start-up */
	crt_info.b_start_up = true;
	crt_info.b_loaded = false;
	crt_info.p_interf = NULL;
	foreach( const QString s_itr, st_s )
	{
		b_found = false;
		i_max = plist.count();
		for ( i = 0; i < i_max; i++ )
		{
			if  ( QString::compare( s_itr, plist.at( i ).s_full_path, Qt::CaseInsensitive ) == 0 )
			{
				b_found = true;
				plist[i].b_start_up = true;
				break;
			}
		}
		if ( b_found == false )
		{
			crt_info.s_full_path = s_itr;
			crt_info.s_name = CpgLibrary::lastPathComp( s_itr );
			plist.append( crt_info );
		}
	}


	return plist;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
OUTCOME					PlgInMan::syncListPlugIns		(
		QList<PlgInInfo> & lst, BBlocks::UserMsg & um )
{
	PlgInInfo			crt_i;
	PlugIn*				plgin;
	bool				b_ok = true;
	OUTCOME				b_var = OUTCOME_OK;
	QStringList			st_s;

	foreach( crt_i, lst )		{
		if ( ( crt_i.b_loaded ) && ( crt_i.p_interf == NULL ) )
		{ /* load */
			b_ok &= OUT_SUCCESS( PlgInMan::fromFile(
									 crt_i.s_full_path,
									 &crt_i.p_interf,
									 um
									 ) );
		}
		else if ( ( crt_i.b_loaded == false ) && ( crt_i.p_interf != NULL ) )
		{ /* unload */
			foreach( plgin, uniq_->plgin_list_ )		{
				if ( plgin->file_ == crt_i.s_full_path )
				{
					Q_ASSERT( plgin == crt_i.p_interf );
					unload( crt_i.p_interf );
					crt_i.b_loaded = false;
					crt_i.p_interf = NULL;
					break;
				}
			}
			/* if we did not found thi one...*/
			if ( crt_i.b_loaded )
			{
				um.add( BBlocks::UserMsgMgr::MSG_WARNING,	QObject::tr(
							"Plugin %1 is not loaded!" )
						.arg( crt_i.s_full_path )
						);
				b_var = OUTCOME_WARNING;
			}
		}

		if ( crt_i.b_start_up )
		{
			st_s.append( crt_i.s_full_path );
		}
	}
	setStartUpSuite( st_s );
	if ( b_ok )
		return b_var;
	else
		return OUTCOME_ERROR;

}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
OUTCOME					PlgInMan::makeStartUp			(
		const QString & the_p, BBlocks::UserMsg & um )
{

	if ( the_p.isEmpty() )
	{
		um.add( BBlocks::UserMsgMgr::MSG_ERROR, QObject::tr(
					"Attempt to make start-up plug-in but"
					"no plug-in file provided.")
				);
		return OUTCOME_ERROR;
	}

	QFile	s_file( the_p );
	if ( ! s_file.exists() )
	{
		um.add( BBlocks::UserMsgMgr::MSG_ERROR, QObject::tr(
					"Attempt to make start-up plug-in with "
					"a non-existing file (%1)")
				.arg( the_p )
				);
		return OUTCOME_ERROR;
	}

	QStringList sl = startUpSuite();
	for ( int i = 0; i < sl.count(); i++ )
	{
		QFile	s_file_c( sl.at( i ) );
		if ( s_file.fileName() == s_file_c.fileName() )
		{
			um.add( BBlocks::UserMsgMgr::MSG_WARNING, QObject::tr(
						"Attempt to make start-up plug-in with "
						"a file (%1) that is already part of start-up suite")
					.arg( the_p )
					);
			return OUTCOME_ERROR;
		}
	}

	sl.append( the_p );
	setStartUpSuite( sl );

	return OUTCOME_OK;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void						PlgInMan::customiseViewMenu ( QMenu * menu )
{
	PlugIn*				plgin;
	foreach( plgin, uniq_->plgin_list_ )	{
		plgin->customiseViewMenu( menu );
	}
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
PlugIn *					PlgInMan::load				(
		const QString & file_path )
{
	BBlocks::UserMsg		um;
	PlugIn *	loaded;

	if ( OUT_SUCCESS( fromFile( file_path, &loaded, um ) ) )
	{
		um.add( BBlocks::UserMsgMgr::MSG_INFO, QObject::tr(
					"The plug-in from file:\n"
					"%1\n"
					"was loaded at index %2.\n" )
				.arg( file_path )
				.arg( indexOf( loaded ) )
				);
	}
	um.show();
	return loaded;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
void						PlgInMan::changePlugInPath	(
		const QString & new_path )
{
	QDir	d( new_path );
	if ( d.exists() == false )
	{
		BBlocks::UserMsg		um;
		um.add( BBlocks::UserMsgMgr::MSG_ERROR, tr(
					"The path %1 is invalid and will not be "
					"set-up as plug-in sarch path" )
				.arg( new_path )
				);
		um.show();
	}
	else
	{
		QSettings	stg;
		QString ab = d.absolutePath();
		stg.setValue( "plgin/dir", ab );
		emit plugInPathChanged( ab );
	}

}
/* ========================================================================= */


/* ------------------------------------------------------------------------- */
PlugIn *						PlgInMan::byName		( const QString & s_name )
{

	PlugIn *	itr;

	if ( s_name.isEmpty() )
		return NULL;

	foreach( itr, uniq_->plgin_list_ )
	{
		if ( QString::compare( itr->name(), s_name, Qt::CaseInsensitive ) == 0 )
		{
			return itr;
		}
	}
	return NULL;
}
/* ========================================================================= */

/* ------------------------------------------------------------------------- */
PlugIn *						PlgInMan::byFile		( const QString & s_name )
{
	PlugIn *	itr;

	if ( s_name.isEmpty() )
		return NULL;

	foreach( itr, uniq_->plgin_list_ )
	{
		if ( QString::compare( itr->file(), s_name, Qt::CaseInsensitive ) == 0 )
		{
			return itr;
		}
	}
	return NULL;
}
/* ========================================================================= */


/*  CLASS    =============================================================== */
//
//
//
//
/* ------------------------------------------------------------------------- */
/* ========================================================================= */
